use halo_model::{ExtensionOperator, GVK};
use std::collections::HashSet;
use std::fmt;
use std::fmt::{Debug, Formatter};

pub trait IndexAttribute {
    type E: ExtensionOperator + GVK;
    fn get_object_type(&self) -> String;
    fn get_values(&self, obj: &Self::E) -> HashSet<String>;
}

pub enum IndexAttributeType<T: ExtensionOperator + GVK> {
    Simple(FunctionalIndexAttribute<T>),
    MultiValue(FunctionalMultiValueIndexAttribute<T>),
}

impl<T: ExtensionOperator + GVK> Debug for IndexAttributeType<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.get_object_type())
    }
}
impl<T: ExtensionOperator + GVK> fmt::Display for IndexAttributeType<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.get_object_type())
    }
}

impl<T: ExtensionOperator + GVK> Clone for IndexAttributeType<T> {
    fn clone(&self) -> Self {
        // 注意：由于包含函数指针，这里只能创建一个新的实例
        // 在实际使用中，可能需要重新设计以支持真正的克隆
        match self {
            IndexAttributeType::Simple(attr) => {
                // 创建一个新的简单属性，但函数指针无法克隆
                // 这里返回一个默认的实现
                simple_attribute(&attr.struct_type, |_| String::new())
            }
            IndexAttributeType::MultiValue(attr) => {
                // 创建一个新的多值属性，但函数指针无法克隆
                // 这里返回一个默认的实现
                multi_value_attribute(&attr.struct_type, |_| HashSet::new())
            }
        }
    }
}

impl<T: ExtensionOperator + GVK> PartialEq for IndexAttributeType<T> {
    fn eq(&self, other: &Self) -> bool {
        // 由于包含函数指针，我们只能比较类型名称
        self.get_object_type() == other.get_object_type()
    }
}

impl<T: ExtensionOperator + GVK> Eq for IndexAttributeType<T> {}

impl<T: ExtensionOperator + GVK> std::hash::Hash for IndexAttributeType<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        // 由于包含函数指针，我们只能哈希类型名称
        self.get_object_type().hash(state);
    }
}

impl<T: ExtensionOperator + GVK> IndexAttributeType<T> {
    fn get_object_type(&self) -> String {
        match self {
            IndexAttributeType::Simple(attr) => attr.get_object_type(),
            IndexAttributeType::MultiValue(attr) => attr.get_object_type(),
        }
    }
    pub(crate) fn get_values(&self, obj: &T) -> HashSet<String> {
        match self {
            IndexAttributeType::Simple(attr) => attr.get_values(obj),
            IndexAttributeType::MultiValue(attr) => attr.get_values(obj),
        }
    }
}

type GetValue<T> = dyn Fn(&T) -> String + Send + Sync;
type GetValues<T> = dyn Fn(&T) -> HashSet<String> + Send + Sync;

pub struct FunctionalIndexAttribute<T: ExtensionOperator + GVK> {
    struct_type: String,
    value_func: Box<GetValue<T>>,
}

impl<T> Debug for FunctionalIndexAttribute<T>
where
    T: ExtensionOperator + GVK,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "FunctionalIndexAttribute {{ struct_type: {} }}",
            self.struct_type
        )
    }
}

impl<T> IndexAttribute for FunctionalIndexAttribute<T>
where
    T: ExtensionOperator + GVK,
{
    type E = T;

    fn get_object_type(&self) -> String {
        self.struct_type.clone()
    }
    fn get_values(&self, obj: &T) -> HashSet<String> {
        let value = (self.value_func)(obj);
        let mut values = HashSet::<String>::new();
        values.insert(value);
        values
    }
}

pub struct FunctionalMultiValueIndexAttribute<T: ExtensionOperator + GVK> {
    struct_type: String,
    value_func: Box<GetValues<T>>,
}

impl<T> IndexAttribute for FunctionalMultiValueIndexAttribute<T>
where
    T: ExtensionOperator + GVK,
{
    type E = T;
    fn get_object_type(&self) -> String {
        self.struct_type.clone()
    }
    fn get_values(&self, obj: &Self::E) -> HashSet<String> {
        let value = (self.value_func)(obj);
        value
    }
}

pub fn simple_attribute<F, T>(struct_type: &str, value_func: F) -> IndexAttributeType<T>
where
    T: ExtensionOperator + GVK,
    F: Fn(&T) -> String,
{
    IndexAttributeType::Simple(FunctionalIndexAttribute {
        struct_type: struct_type.to_string(),
        value_func: Box::new(value_func),
    })
}

pub fn multi_value_attribute<F, T>(struct_type: &str, value_func: F) -> IndexAttributeType<T>
where
    T: ExtensionOperator + GVK,
    F: Fn(&T) -> HashSet<String>,
{
    IndexAttributeType::MultiValue(FunctionalMultiValueIndexAttribute {
        struct_type: struct_type.to_string(),
        value_func: Box::new(value_func),
    })
}

pub struct IndexAttributeFactory;

impl IndexAttributeFactory {
    pub fn simple_attribute<F, T>(struct_type: String, value_func: F) -> IndexAttributeType<T>
    where
        T: ExtensionOperator + GVK,
        F: Fn(&T) -> String + 'static,
    {
        simple_attribute(&struct_type, value_func)
    }

    pub fn multi_value_attribute<F, T>(struct_type: String, value_func: F) -> IndexAttributeType<T>
    where
        T: ExtensionOperator + GVK,
        F: Fn(&T) -> HashSet<String> + 'static,
    {
        multi_value_attribute(&struct_type, value_func)
    }
}

#[cfg(test)]
mod tests {
    use crate::extension::index::index::query::index_attribute::{
        IndexAttribute, IndexAttributeFactory,
    };
    use crate::tests::index_view_data_set::FakeExtension;
    use halo_model::Metadata;
    use std::collections::HashSet;

    #[test]
    fn multi_value_attribute() {
        let attribute = IndexAttributeFactory::multi_value_attribute(
            "FakeExtension".to_string(),
            |e: &FakeExtension| e.tags.clone(),
        );
        assert_eq!(attribute.get_object_type(), "FakeExtension".to_string());

        let mut metadata = Metadata::default();
        metadata.set_name("fake-name-1".to_string());
        let mut set = HashSet::new();
        set.insert("tag1".to_string());
        set.insert("tag2".to_string());
        let mut extension = FakeExtension::new();
        extension.email = "".to_string();
        extension.tags = set.clone();

        assert_eq!(attribute.get_values(&extension), set);
    }
}
